/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Salesman.gui;

import Salesman.ArrayUtils;
import Salesman.City;
import Salesman.Data.DataProvider;
import Salesman.Distances;
import java.awt.Component;
import java.util.EventObject;
import javax.swing.AbstractListModel;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JTable;
import javax.swing.ListCellRenderer;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableColumnModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumn;
import org.rlpw.swing.JGroupPanel;
import org.rlpw.swing.Model;
import org.rlpw.swing.event.ModelEvent;
import org.rlpw.swing.event.ModelListener;
import org.rlpw.swing.event.ModelReplacedEvent;

/**
 *
 * @author weinpau
 */
public class MatrixTable extends JGroupPanel {

    private JTable table = new JTable();
    private Distances distances;
    private Model<City> model;
    private AbstractTableModel atm;

    public MatrixTable() {
        this(DataProvider.getCityData());
    }

    public MatrixTable(Model<City> model, Distances distances) {
        this(model);
        setDistances(distances);
    }

    public MatrixTable(Model<City> model) {
        super(2, 2, false, false);
        setModel(model);

        table.getColumnModel().addColumnModelListener(new TableColumnModelListener() {

            @Override
            public void columnAdded(TableColumnModelEvent e) {
                TableColumn c = table.getColumnModel().getColumn(e.getToIndex());
                c.setPreferredWidth(40);
                c.setMinWidth(40);
                c.setMaxWidth(40);
            }

            @Override
            public void columnRemoved(TableColumnModelEvent e) {
            }

            @Override
            public void columnMoved(TableColumnModelEvent e) {
            }

            @Override
            public void columnMarginChanged(ChangeEvent e) {
            }

            @Override
            public void columnSelectionChanged(ListSelectionEvent e) {
            }
        });



        atm = new AbstractTableModel() {

            @Override
            public int getRowCount() {
                if (getDistances() == null) {
                    return 0;
                } else {

                    checkRange();
                    return getDistances().data().length;
                }
            }

            @Override
            public int getColumnCount() {
                checkRange();
                return getModel().size();
            }

            @Override
            public Object getValueAt(int rowIndex, int columnIndex) {
                return getDistances().get(rowIndex, columnIndex);
            }

            @Override
            public String getColumnName(int column) {
                return getModel().get(column).getShortName();
            }

            @Override
            public Class<?> getColumnClass(int columnIndex) {
                return Double.class;
            }

            @Override
            public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
                if (aValue instanceof Double) {
                    double d = ((Double) aValue).doubleValue();
                    getDistances().set(rowIndex, columnIndex, d);
                    revalidate();
                    repaint();
                }
            }

            @Override
            public boolean isCellEditable(int rowIndex, int columnIndex) {
                return (getDistances().get(rowIndex, columnIndex) != Double.POSITIVE_INFINITY);
            }
        };

        table.setModel(atm);


        model.addModelListener(new ModelListener<City>() {

            @Override
            public void itemAdded(ModelEvent<City> e) {
                atm.fireTableStructureChanged();
            }

            @Override
            public void itemRemoved(ModelEvent<City> e) {
                atm.fireTableStructureChanged();
            }

            @Override
            public void itemChanged(ModelEvent<City> e) {
                atm.fireTableStructureChanged();
            }

            @Override
            public void itemReplaced(ModelReplacedEvent<City> e) {
            }

            @Override
            public void allItemsRemoved(EventObject e) {
            }
        });

        JList rowHeader = new JList(new AbstractListModel() {

            @Override
            public int getSize() {
                return getModel().size();
            }

            @Override
            public Object getElementAt(int index) {
                return getModel().get(index).getName();
            }
        });

        rowHeader.setOpaque(false);
        rowHeader.setBackground(UIManager.getColor("TableHeader.background"));
        rowHeader.setFixedCellWidth(120);
        rowHeader.setFixedCellHeight(table.getRowHeight());
        rowHeader.setCellRenderer(new RowHeaderRenderer(table));

        table.getTableHeader().setReorderingAllowed(false);

        setComponent(1, 0, table.getTableHeader());
        setComponent(0, 1, rowHeader);
        setComponent(1, 1, table);
    }

    public Distances getDistances() {
        return distances;
    }

    public void setDistances(Distances distances) {
        this.distances = distances;
    }

    public Model<City> getModel() {
        return model;
    }

    public void setModel(Model<City> model) {
        this.model = model;
    }

    private class RowHeaderRenderer extends JLabel implements ListCellRenderer {

        RowHeaderRenderer(JTable table) {
            JTableHeader tableHeader = table.getTableHeader();
            setBorder(UIManager.getBorder("TableHeader.cellBorder"));
            setHorizontalAlignment(TRAILING);
            setForeground(tableHeader.getForeground());
            setBackground(tableHeader.getBackground());
            setOpaque(true);
            setFont(tableHeader.getFont());
        }

        @Override
        public Component getListCellRendererComponent(JList list, Object value, int index,
                boolean fSelected, boolean fCellHasFocus) {
            setText((value == null) ? "" : value.toString() + " ");
            return this;
        }
    }

    private void checkRange() {

        if (getDistances() == null || getModel() == null) {
            return;
        }
        if (getDistances().data().length != getModel().size()) {
            setDistances(DataProvider.getDistances());
        }
    }
}
